package trading.formulas;

import java.util.Calendar;

import trading.Parameters;
import trading.data.Controller;
import trading.data.IDataType;
import trading.data.IStockTickDataCollection;
import trading.formulas.functions.EMA;
import trading.formulas.functions.IDataFunctionProvider;
import trading.formulas.functions.TradingFunctionException;

/**
 * performance tuning: to establish the ema 
 * @author Samuel
 *
 */
public class MACD implements IFomula {
	
	protected MACD() {}
	
	static public MACD getMACD()
	{
		return new MACD();
	}

	public static final String DIF = "DIF";
	public static final String DEA = "DEA";
	public static final String MACD = "MACD";
	
	public static final int SHORT_IDX = 0;
	public static final int LONG_IDX = 1;
	public static final int M_IDX = 2;
	
	private Parameters PARAM = new Parameters(new int[][]{
				/*short*/{300,1,59,1}, 
				/*long*/{300, 1, 223,1}, 
				/*M*/{20, 1, 2, 1}}); 

	@Override
	public Parameters getParameters() {
		return PARAM;
	}

	@Override
	public Object getValue(String name, IStockTickDataCollection dataCollection, int index)
			throws TradingFunctionException {
		
		if(name.equalsIgnoreCase(DEA))
		{
			return getDEA(dataCollection, index, IDataType.CLOSE_NAME);
		}
		else if(name.equalsIgnoreCase(DIF))
		{
			return getDIF(dataCollection, index, IDataType.CLOSE_NAME);
		}
		else if(name.equalsIgnoreCase(MACD))
		{
			return getMACD(dataCollection, index, IDataType.CLOSE_NAME);
		}
		else
		{
			throw new TradingFunctionException("Unsupported return value name of MACD" );
		}
			
	}

	/**
	 * DIF = EMA(c,short)-EMA(c,long)
	 * @param dataCollection
	 * @param index
	 * @return
	 */
	protected float getDIF(IStockTickDataCollection dataCollection, int index, String datatype)
	{
		int param_short = this.getParameters().getParamValue(SHORT_IDX);
		int param_long = this.getParameters().getParamValue(LONG_IDX);
		
		EMA emaShort = EMA.getEMAInstance();
		emaShort.getParameters().setParamValue(EMA.CYCLE_IDX, param_short);
		float shortEma = emaShort.getValue(datatype, dataCollection, index);
		
		EMA emaLong = EMA.getEMAInstance();
		emaLong.getParameters().setParamValue(EMA.CYCLE_IDX, param_long);
		float longEma = emaLong.getValue(datatype, dataCollection, index);
		
		return (shortEma - longEma);
	}
	
	/**
	 * DEA = EMA(DIF, m)
	 * @param dataCollection
	 * @param index
	 * @return
	 */
	protected float getDEA(IStockTickDataCollection dataCollection, int index, String datatype)
	{
		int param_m = this.getParameters().getParamValue(M_IDX);
		
		EMA ema = EMA.getEMAInstance();
		ema.getParameters().setParamValue(EMA.CYCLE_IDX, param_m);
		return ema.getValue(datatype, dataCollection, index, getDIFFunctionProvider());
	}
	
	/**
	 * MACD:=2*(DIF-DEA);
	 * @param dataCollection
	 * @param index
	 * @param datatype
	 * @return
	 */
	protected float getMACD(IStockTickDataCollection dataCollection, int index, String datatype)
	{
		return 2 * (getDIF(dataCollection, index, datatype) - getDEA(dataCollection, index, datatype));
	}
	
	public IDataFunctionProvider getDIFFunctionProvider()
	{
		return new IDataFunctionProvider(){

			@Override
			public float getValue(String dataType,
					IStockTickDataCollection dataCollection, int index) {
				return MACD.this.getDIF(dataCollection, index, dataType);
			}
			
			@Override
			public String getName() {
				//TODO: remove m in params key
				return DIF + "_" + MACD.this.PARAM.getParamsAsKey();
			}
			};
			
	
	}
	
	public static void main(String[] args) 
	{
		Calendar cal = Controller.createZeroCaledar();
		cal.set(2009, 05 - 1, 15);
		IStockTickDataCollection dataCollection = Controller.getTickData();
		int index = Controller.getIndex(dataCollection, cal, true);
		System.out.println(index);
//		index = 10;
		MACD macd = getMACD();;
		Parameters params = macd.getParameters();
		params.setParamValue(LONG_IDX, 223);
		params.setParamValue(SHORT_IDX, 59);
		params.setParamValue(M_IDX, 2);
		long currentTime = System.currentTimeMillis();
		System.out.println(currentTime);
		System.out.println(macd.getValue(DIF, dataCollection, index));
		System.out.println(System.currentTimeMillis() - currentTime);
		currentTime = System.currentTimeMillis();
		System.out.println(currentTime);
		
		System.out.println(macd.getValue(DEA, dataCollection, index));
		System.out.println(System.currentTimeMillis() - currentTime);
		currentTime = System.currentTimeMillis();
		System.out.println(currentTime);
		
		System.out.println(macd.getValue(MACD, dataCollection, index));
		System.out.println(System.currentTimeMillis() - currentTime);
		System.out.println(currentTime);
	}
}
